#ifndef TEST_STEERINGGAME_HPP
#define TEST_STEERINGGAME_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <vector>
#include <climits>
#include <cfloat>

#include <SmegCore/Game/DefaultGameImpl.hpp>
#include <SmegAI/Movement/PointPath.hpp>

#include "Test_SteeringEntity.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////
#define MAX_VALUES_TO_CHANGE 26

namespace sf {
    class Font;
    class Text;
}

class Wall;
class Obstacle;

class Test_SteeringGameImpl : public smeg::core::DefaultGameImpl
{
public:
    ////////////////////////////////////////////////////////////
    /// Constructor/Destructor
    ////////////////////////////////////////////////////////////
                            Test_SteeringGameImpl();
    virtual                 ~Test_SteeringGameImpl();

    DECLARE_CLONE_FUNC( Test_SteeringGameImpl, smeg::core::DefaultGameImpl )

    ////////////////////////////////////////////////////////////
    /// Init the game
    ///
    /// \param _pRenderWindow
    ///
    ////////////////////////////////////////////////////////////
    virtual void            Init( sf::RenderWindow* _pRenderWindow );

    ////////////////////////////////////////////////////////////
    /// DeInit the game
    ///
    ////////////////////////////////////////////////////////////
    virtual void            DeInit();

    ////////////////////////////////////////////////////////////
    /// Manage an incoming event
    ////////////////////////////////////////////////////////////
    virtual void            ManageEvent( const sf::Event& _Event );

    ////////////////////////////////////////////////////////////
    /// Call all the Update in the other systems
    ////////////////////////////////////////////////////////////
    virtual void            Update( float _fDeltaTime );

    ////////////////////////////////////////////////////////////
    /// Call all the Render in the other systems
    ////////////////////////////////////////////////////////////
    virtual void            Render();

    static char             GetKeyCodeName( sf::Keyboard::Key _KeyCode );
    
    // Value changing
    enum eValueChangeType {
        eDecrease,
        eIncrease
    };
    struct ValueChange {
        typedef std::string (Test_SteeringGameImpl::*DisplayValueFunc)() const;

        eValueChangeType    m_Type;
        sf::Keyboard::Key   m_Key;
        std::string         m_Name;
        
        int                 m_iThreshold;
        float               m_fThreshold;
        bool                m_bLoop;
        
        int                 *m_pIntVal;
        float               *m_pFloatVal;
        bool                *m_pBoolVal;
        
        int                 m_iAmount;
        float               m_fAmount;

        DisplayValueFunc    m_pDisplayValueFunc;
        Test_SteeringGameImpl* m_pInstance;

        ValueChange         *m_pSisterValue;
        bool                m_bNeedDisplay;

        ValueChange();

        void Reset();        
        void Setup( sf::Keyboard::Key   _Key,
                    const std::string&  _Name,
                    int                 *_pIntVal,
                    eValueChangeType    _Type,
                    DisplayValueFunc    _pDisplayValueFunc = NULL,
                    Test_SteeringGameImpl* _pInstance = NULL,
                    int                 _iAmount = 1,
                    int                 _iThreshold = INT_MIN,
                    bool                _bLoop = false,
                    ValueChange         *_pSisterValue = NULL );
        void Setup( sf::Keyboard::Key   _Key,
                    const std::string&  _Name,
                    float               *_pFloatVal,
                    eValueChangeType    _Type,
                    float               _fAmount = 1.0f,
                    float               _fThreshold = -FLT_MAX,
                    bool                _bLoop = false,
                    ValueChange         *_pSisterValue = NULL );
        void Setup( sf::Keyboard::Key   _Key,
                    const std::string&  _Name,
                    bool                *_pBoolVal );
        void SetupSisterFloat(  ValueChange*        _pSister,
                                sf::Keyboard::Key   _Key,
                                float               _fThreshold = -FLT_MAX );
        void SetupSisterInt(    ValueChange*        _pSister,
                                sf::Keyboard::Key   _Key,
                                int                 _iThreshold = INT_MIN );

        bool IsValid() const;
        bool Compute( sf::Keyboard::Key _Code );

        friend std::ostream& operator<<( std::ostream& o, const ValueChange& _ValueToDisplay );
    };
private:
    void                    ResetAllValuesToChange();
    void                    SetupValuesToChange();

    ////////////////////////////////////////////////////////////
    /// Behaviors
    ////////////////////////////////////////////////////////////
    void                    CreateMouse();
    Test_SteeringEntity*    CreateEntity();
    void                    RemoveEntity();
    void                    RemoveAllEntities();
    void                    AddEntities();
    void                    ChangeComputeMethod( int _iNewComputeMethod );
    void                    ChangeBehavior( int _iNewBehavior );
    void                    SetEntityBehavior( Test_SteeringEntity* _pEntity );
    void                    ChangeEntitySmoothing();
    
    void                    AddObstacles();
    void                    AddWalls();

    std::string             GetBehaviorName() const;
    std::string             GetComputeMethodName() const;
    std::string             GetLoopTypeName() const;
    std::string             GetDecelerationTypeName() const;

    // Info
    sf::Font*               m_pFont;
    sf::Text*               m_pInfo;

    bool                    m_bShowDebug;
    smeg::core::MovableEntity* m_pMouseEntity;
    std::vector< Test_SteeringEntity* > m_Entities;

    bool                    m_bPause;

    ValueChange             m_ValuesToChange[MAX_VALUES_TO_CHANGE];

    // Behavior
    enum eBehaviors {
        eSeek               = 0,
        eFlee               = 1,
        ePursue             = 2,
        eOffsetPursue       = 3,
        eEvade              = 4,
        eArrive             = 5,
        eWander             = 6,
        eInterpose          = 7,
        ePathFollowing      = 8,
        eSeparation         = 9,
        eAlignment          = 10,
        eCohesion           = 11,
        eFlocking           = 12,
        eWallAvoidance      = 13,
        eObstacleAvoidance  = 14,
        eMAX_BEHAVIORS
    };

    smeg::ai::SteeringManager::eComputeMethod   m_eCurrentComputeMethod;
    eBehaviors                                  m_eCurrentBehavior;
    float                                       m_fBehaviorWeight;
    bool                                        m_bIsSmoothingOn;

    // Offset Pursue
    sf::Vector2f                                m_OffsetPursue;
    
    // Arrive
    int                                         m_iArrivalDeceleration;
    float                                       m_fArriveThreshold;

    // Wander
    float                                       m_fWanderRadius;
    float                                       m_fWanderDistance;
    float                                       m_fWanderJitter;

    // Path following
    smeg::ai::PointPath                         m_PathToFollow;
    float                                       m_fDistToWaypoint;
    int                                         m_iLoopType;
    bool                                        m_bReversePath;

    int                                         m_iNumEntities;
    int                                         m_iAddingEntities;
    int                                         m_iAddingObstacles;

    // Separation / Alignment / Cohesion
    float                                       m_fViewRadius;

    // Flocking
    float                                       m_fSeparationWeight;
    float                                       m_fAlignmentWeight;
    float                                       m_fCohesionWeight;

    // Wall Avoidance
    std::vector< Wall* >                        m_Walls;
    float                                       m_fFeelerLength;

    // Obstacle Avoidance
    std::vector< Obstacle* >                    m_Obstacles;
    float                                       m_fBrakingWeight;
};

inline char Test_SteeringGameImpl::GetKeyCodeName( sf::Keyboard::Key _KeyCode ) {
    return char(65 + _KeyCode);
}

inline std::string Test_SteeringGameImpl::GetBehaviorName() const {
    switch( m_eCurrentBehavior ) {
    case eSeek:
        return "Seek";
    case eFlee:
        return "Flee";
    case ePursue:
        return "Pursue";
    case eOffsetPursue:
        return "Offset Pursue";
    case eEvade:
        return "Evade";
        break;
    case eArrive:
        return "Arrive";
    case eWander:
        return "Wander";
    case eInterpose:
        return "Interpose";
    case ePathFollowing:
        return "Path Following";
    case eSeparation:
        return "Separation";
    case eAlignment:
        return "Alignment";
    case eCohesion:
        return "Cohesion";
    case eFlocking:
        return "Flocking";
    case eWallAvoidance:
        return "Wall Avoidance";
    case eObstacleAvoidance:
        return "Obstacle Avoidance";
    default:
        return "none";
    }
}

inline std::string Test_SteeringGameImpl::GetComputeMethodName() const {
    switch( m_eCurrentComputeMethod ) {
    case smeg::ai::SteeringManager::eWeighedSums:
        return "Weighed Sums";
    case smeg::ai::SteeringManager::ePrioritized:
        return "Prioritized";
    case smeg::ai::SteeringManager::eDithered:
        return "Dithered";
    default:
        return "none";
    }
}

inline std::string Test_SteeringGameImpl::GetLoopTypeName() const {
    switch( m_iLoopType ) {
    case smeg::ai::PointPath::eLoop:
        return "Base Loop";
    case smeg::ai::PointPath::eNoLoop:
        return "No Loop";
    case smeg::ai::PointPath::eBackAndForth:
        return "Back and Forth";
    default:
        return "none";
    }
}

inline std::string Test_SteeringGameImpl::GetDecelerationTypeName() const {
    switch( m_iArrivalDeceleration ) {
    case smeg::ai::SteeringManager::eSlow:
        return "Slow";
    case smeg::ai::SteeringManager::eNormal :
        return "Normal";
    case smeg::ai::SteeringManager::eFast:
        return "Fast";
    default:
        return "none";
    }
}

#include <SmegTools/Debug/DebugNewOff.hpp>

#endif // TEST_STEERINGGAME_HPP
